﻿using System;
using System.IO;
using System.Configuration;

namespace OJS.Worker
{        
    public class Judger
    {
        /// <summary>
        /// Pending file path
        /// </summary>
        private string PENDING_FILE_PATH;

        /// <summary>
        /// ACM test data dir
        /// </summary>
        private string ACM_DATA_DIR;

        /// <summary>
        /// OI test data dir
        /// </summary>
        private string OI_DATA_DIR;

        /// <summary>
        /// Create new instance of OJS.Worker.Judger class
        /// </summary>
        public Judger()
        {            
            // Load specified data
        }

        /// <summary>
        /// Run judge
        /// </summary>
        /// <param name="submitInfo">Submit info</param>
        /// <returns>Judge result</returns>
        public JudgeResult DoJudge(SubmitInfo submitInfo)
        {
            // Tạo file mã nguồn
            var fileExt = 0 == submitInfo.Language ? "CPP" : "JAVA";
            switch (submitInfo.Language)
            {
                case 0: fileExt = "c";
                    break;
                case 1: fileExt = "cpp";
                    break;
                case 2: fileExt = "java";
                    break;
                case 3: fileExt = "cs";
                    break;
                default : fileExt = "";
                    break;
            }

            var file = PENDING_FILE_PATH + "." + fileExt;
            File.WriteAllText( file, submitInfo.SourceCode );
            string result = "", time = "0", memory = "0";
            
            var compile = CompileFactory.Create(submitInfo.Language).Compile(file);
            
            if (compile.Success)
            {                
                var executable = new Runner(compile.ExecutableFile, submitInfo.SpecifiedTime, submitInfo.SpecifiedMemorySize);
                if (submitInfo.IsACM)
                {
                    string acmInput = ACM_DATA_DIR + submitInfo.ProblemCode + ".inp";
                    if (File.Exists(acmInput))
                    {
                        var eResult = executable.Run(acmInput);
                        if (eResult.Success)
                        {                            
                            string answerPath = ACM_DATA_DIR + submitInfo.ProblemCode + ".out";
                            if (File.Exists(answerPath))
                            {                                
                                var chResult = Check(eResult.Output, answerPath);
                                if (chResult)
                                {
                                    result = "ACCEPTED";
                                    time = eResult.ProcessTime.ToString();
                                    memory = eResult.UsedMemory.ToString();
                                }
                                else
                                {
                                    result = "WRONG ANSWER";
                                    time = eResult.ProcessTime.ToString();
                                    memory = eResult.UsedMemory.ToString();
                                }
                            }
                            else
                            {
                                result = "RESULT FILE NOT FOUND";
                            }                            
                        }
                        else
                        {
                            result = eResult.ErrorType;
                            time = eResult.ProcessTime == null ? " 0 " : eResult.ProcessTime.ToString();
                            memory = eResult.UsedMemory == null ? " 0 " : eResult.UsedMemory.ToString();
                        }                        
                    }
                    else
                    {
                        result = "INPUT FILE NOT FOUND";
                    }                 
                }
                else
                {
                    string inputDir = OI_DATA_DIR + submitInfo.ProblemCode + @"\INPUT\";
                    string outputDir = OI_DATA_DIR + submitInfo.ProblemCode + @"\OUTPUT\";
                    if (Directory.Exists(inputDir) && Directory.Exists(outputDir))
                    {
                        string[] inputs = Directory.GetFiles(inputDir);
                        string[] outputs = Directory.GetFiles(outputDir);
                        if (inputs.Length == outputs.Length)
                        {
                            double? totalTime = 0, totalMemory = 0;
                            int acceptedTest = 0;
                            for (int i = 0; i < inputs.Length; ++i)
                            {
                                var eResult = executable.Run(inputs[i]);
                                if (eResult.Success)
                                {
                                    bool chResult = this.Check(eResult.Output, outputs[i]);
                                    if (chResult)
                                    {
                                        totalTime += eResult.ProcessTime;
                                        totalMemory += eResult.UsedMemory;
                                        acceptedTest++;
                                    }
                                }
                            }
                            result = (acceptedTest * 100 / inputs.Length).ToString();
                            time = totalTime.ToString();
                            memory = totalMemory.ToString();
                        }
                        else
                        {
                            result = "INPUTs AND OUTPUTs NOT EQUALS";
                        }
                    }
                    else
                    {
                        result = "DATA NOT FOUND";
                    }
                }                
            }
            else
            {
                result = "COMPILE ERROR";
            }

            return new JudgeResult {
                SubmitTime = DateTime.Now,
                UserId = submitInfo.UserId,
                ProblemCode = submitInfo.ProblemCode,
                Result = result,
                TimeConsume = time,
                MemoryConsume = memory,
                Language = submitInfo.Language
            };
        }

        /// <summary>
        /// Hàm Check giúp so sánh kết quả cho User tạo ra và kết quả của bộ test
        /// </summary>
        /// <param name="output">Kết quả do user tạo ra</param>
        /// <param name="answerPath">Đường dẫn đến file ouput</param>
        /// <returns></returns>
        private bool Check(string output, string answerPath)
        {
            string answer = File.ReadAllText(answerPath);
            if (output != answer) return false;            
            return true;
        }
    }
}